package deanandjames.image;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.List;

public class ImageLoader {
	private final static String IMAGE_DIR = "resources/images/";
	private final GraphicsConfiguration graphicsConfiguration;
	private final Map<String, List<String>> imageNames;
	private final Map<String, List<BufferedImage>> imagesMap;

	public ImageLoader(String imagesFileName) {
		imagesMap = new HashMap<String, List<BufferedImage>>();
		imageNames = new HashMap<String, List<String>>();
		final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		graphicsConfiguration = ge.getDefaultScreenDevice().getDefaultConfiguration();

		final String imageFile = IMAGE_DIR + imagesFileName;
		System.out.println("Reading file: " + imageFile);
		try {
			final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(imageFile)));
			String line;
			while ((line = reader.readLine()) != null) {
				line = line.trim();
				if (line.length() == 0 || line.startsWith("//")) {
					continue;
				}
				char ch = Character.toLowerCase(line.charAt(0));
				if (ch == 'o') {
					getFileNameImage(line);
				} else if (ch == 'n') {
					getNumberedImages(line);
				} else if (ch == 's') {
					getStripImages(line);
				} else if (ch == 'g') {
					getGroupImages(line);
				} else {
					System.out.println("Do not recognize line: " + line);
				}
			}
			reader.close();
		} catch (final IOException e) {
			System.out.println("Error reading file: " + imageFile);
			System.exit(1);
		}
	}

	public BufferedImage getImage(String imageName) {
		final List<BufferedImage> images = imagesMap.get(imageName);
		if (images == null) {
			System.out.println("No image(s) stored under " + imageName);
			return null;
		}
		return images.get(0);
	}

	public BufferedImage getImage(String name, int position) {
		final List<BufferedImage> images = imagesMap.get(name);
		if (images == null) {
			System.out.println("No image(s) stored under " + name);
			return null;
		}
		final int size = images.size();
		if (position < 0) {
			return images.get(0);
		} else if (position >= size) {
			final int newPosition = position % size;
			return images.get(newPosition);
		}
		return images.get(position);
	}

	public List<BufferedImage> getImages(String imageName) {
		final List<BufferedImage> images = imagesMap.get(imageName);
		if (images == null) {
			System.out.println("No image(s) stored under " + imageName);
			return null;
		}
		System.out.println("Returning all images stored under " + imageName);
		return images;
	}

	public boolean isLoaded(String imageName) {
		return imagesMap.get(imageName) != null;
	}

	public int loadStripImages(String fileName, int number) {
		final String name = getPrefix(fileName);
		if (imagesMap.containsKey(name)) {
			System.out.println("Error: " + name + "already used");
			return 0;
		}
		final BufferedImage[] strip = loadStripImageArray(fileName, number);
		if (strip == null) {
			return 0;
		}
		final List<BufferedImage> imsList = new ArrayList<BufferedImage>();
		int loadCount = 0;
		System.out.print("  Adding " + name + "/" + fileName + "... ");
		for (int i = 0; i < strip.length; i++) {
			loadCount++;
			imsList.add(strip[i]);
			System.out.print(i + " ");
		}
		System.out.println();
		if (loadCount == 0) {
			System.out.println("No images loaded for " + name);
		} else {
			imagesMap.put(name, imsList);
		}
		return loadCount;
	}

	public int numImages(String name) {
		final List<BufferedImage> images = imagesMap.get(name);
		if (images == null) {
			System.out.println("No image(s) stored under " + name);
			return 0;
		}
		return images.size();
	}

	private BufferedImage createBufferedImage(Image im, int width, int height) {
		final BufferedImage copy = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		final Graphics2D g2d = copy.createGraphics();
		g2d.drawImage(im, 0, 0, null);
		g2d.dispose();
		return copy;
	}

	private void getFileNameImage(String line) {
		final StringTokenizer tokens = new StringTokenizer(line);
		if (tokens.countTokens() != 2) {
			System.out.println("Wrong no. of arguments for " + line);
		} else {
			tokens.nextToken();
			System.out.print("o Line: ");
			loadSingleImage(tokens.nextToken());
		}
	}

	private void getGroupImages(String line) {
		final StringTokenizer tokens = new StringTokenizer(line);
		if (tokens.countTokens() < 3) {
			System.out.println("Wrong no. of arguments for " + line);
		} else {
			tokens.nextToken();
			System.out.print("g Line: ");
			final String name = tokens.nextToken();
			final List<String> fileNames = new ArrayList<String>();
			fileNames.add(tokens.nextToken());
			while (tokens.hasMoreTokens()) {
				fileNames.add(tokens.nextToken());
			}
			loadGroupImages(name, fileNames);
		}
	}

	private int getGroupPosition(String name, String fnmPrefix) {
		final List<String> groupNames = imageNames.get(name);
		if (groupNames == null) {
			System.out.println("No group names for " + name);
			return -1;
		}
		for (int i = 0; i < groupNames.size(); i++) {
			String nm = groupNames.get(i);
			if (nm.equals(fnmPrefix)) {
				return i;
			}
		}
		System.out.println("No " + fnmPrefix + " group name found for " + name);
		return -1;
	}

	private void getNumberedImages(String line) {
		final StringTokenizer tokens = new StringTokenizer(line);
		if (tokens.countTokens() != 3) {
			System.out.println("Wrong no. of arguments for " + line);
		} else {
			tokens.nextToken();
			System.out.print("n Line: ");
			final String fnm = tokens.nextToken();
			int number;
			try {
				number = Integer.parseInt(tokens.nextToken());
			} catch (final Exception e) {
				System.out.println("Number is incorrect for " + line);
				number = -1;
			}
			loadNumImages(fnm, number);
		}
	}

	private String getPrefix(String fnm) {
		int posn;
		if ((posn = fnm.lastIndexOf(".")) == -1) {
			System.out.println("No prefix found for filename: " + fnm);
			return fnm;
		} else {
			return fnm.substring(0, posn);
		}
	}

	private void getStripImages(String line) {
		final StringTokenizer tokens = new StringTokenizer(line);
		if (tokens.countTokens() != 3) {
			System.out.println("Wrong no. of arguments for " + line);
		} else {
			tokens.nextToken();
			System.out.print("s Line: ");
			final String fnm = tokens.nextToken();
			int number = -1;
			try {
				number = Integer.parseInt(tokens.nextToken());
			} catch (final Exception e) {
				System.out.println("Number is incorrect for " + line);
			}
			loadStripImages(fnm, number);
		}
	}

	private int loadGroupImages(String fileName, List<String> fileNames) {
		if (imagesMap.containsKey(fileName)) {
			System.out.println("Error: " + fileName + "already used");
			return 0;
		}
		if (fileName.isEmpty()) {
			System.out.println("List of filenames is empty");
			return 0;
		}
		final List<String> names = new ArrayList<String>();
		final List<BufferedImage> images = new ArrayList<BufferedImage>();
		int loadCount = 0;
		System.out.println("  Adding to " + fileName + "...");
		System.out.print("  ");
		for (int i = 0; i < fileNames.size(); i++) {
			final String fnm = fileNames.get(i);
			final String imageName = getPrefix(fnm);
			BufferedImage bufferedImage;
			if ((bufferedImage = loadImage(fnm)) != null) {
				loadCount++;
				images.add(bufferedImage);
				names.add(imageName);
				System.out.print(imageName + "/" + fnm + " ");
			}
		}
		System.out.println();
		if (loadCount == 0) {
			System.out.println("No images loaded for " + fileName);
		} else {
			imagesMap.put(fileName, images);
			imageNames.put(fileName, names);
		}
		return loadCount;
	}

	private BufferedImage loadImage(String fileName) {
		try {
			final BufferedImage bufferedImage = ImageIO.read(new FileInputStream(IMAGE_DIR + fileName));
			final int transparency = bufferedImage.getColorModel().getTransparency();
			final BufferedImage copy = graphicsConfiguration.createCompatibleImage(bufferedImage.getWidth(), bufferedImage.getHeight(), transparency);
			final Graphics2D graphics = copy.createGraphics();
			graphics.drawImage(bufferedImage, 0, 0, null);
			graphics.dispose();
			return copy;
		} catch (final IOException e) {
			System.out.println("Load Image error for " + IMAGE_DIR + "/" + fileName + ":\n" + e);
			return null;
		}
	}

	private int loadNumImages(String fileName, int number) {
		final int starPosn = fileName.lastIndexOf("*");
		String prefix;
		String postfix;
		if (starPosn == -1) {
			System.out.println("No '*' in filename: " + fileName);
			prefix = getPrefix(fileName);
			postfix = null;
		} else {
			prefix = fileName.substring(0, starPosn);
			postfix = fileName.substring(starPosn + 1);
		}
		if (imagesMap.containsKey(prefix)) {
			System.out.println("Error: " + prefix + "already used");
			return 0;
		}
		return loadNumImages(prefix, postfix, number);
	}

	private int loadNumImages(String prefix, String postfix, int number) {
		BufferedImage image;
		final List<BufferedImage> images = new ArrayList<BufferedImage>();
		int loadCount = 0;
		String imageFileName;
		if (number <= 0) {
			System.out.println("Error: Number <= 0: " + number);
			imageFileName = prefix + postfix;
			if ((image = loadImage(imageFileName)) != null) {
				loadCount++;
				images.add(image);
				System.out.println("  Stored " + prefix + "/" + imageFileName);
			}
		} else {
			System.out.print("  Adding " + prefix + "/" + prefix + "*" + postfix + "... ");
			for (int i = 0; i < number; i++) {
				imageFileName = prefix + i + postfix;
				if ((image = loadImage(imageFileName)) != null) {
					loadCount++;
					images.add(image);
					System.out.print(i + " ");
				}
			}
			System.out.println();
		}
		if (loadCount == 0) {
			System.out.println("No images loaded for " + prefix);
		} else {
			imagesMap.put(prefix, images);
		}
		return loadCount;
	}

	private boolean loadSingleImage(String fnm) {
		final String name = getPrefix(fnm);
		if (imagesMap.containsKey(name)) {
			System.out.println("Error: " + name + "already used");
			return false;
		}
		final BufferedImage bi = loadImage(fnm);
		if (bi != null) {
			final List<BufferedImage> imsList = new ArrayList<BufferedImage>();
			imsList.add(bi);
			imagesMap.put(name, imsList);
			System.out.println("  Stored " + name + "/" + fnm);
			return true;
		} else {
			return false;
		}
	}

	private BufferedImage[] loadStripImageArray(String fnm, int number) {
		if (number <= 0) {
			System.out.println("number <= 0; returning null");
			return null;
		}
		BufferedImage stripIm;
		if ((stripIm = loadImage(fnm)) == null) {
			System.out.println("Returning null");
			return null;
		}
		final int width = stripIm.getWidth() / number;
		final int height = stripIm.getHeight();
		final int transparency = stripIm.getColorModel().getTransparency();
		final BufferedImage[] strip = new BufferedImage[number];
		Graphics2D graphics;
		for (int i = 0; i < number; i++) {
			strip[i] = graphicsConfiguration.createCompatibleImage(width, height, transparency);
			graphics = strip[i].createGraphics();
			graphics.drawImage(stripIm, 0, 0, width, height, i * width, 0, i * width + width, height, null);
			graphics.dispose();
		}
		return strip;
	}
}
